#ifndef BARCODE_COUNTRY_CODE_HPP_INLCUDED
#define BARCODE_COUNTRY_CODE_HPP_INLCUDED

#include <boost/cstdint.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>

#include <algorithm>
#include <list>
#include <string>
#include <functional>

namespace barcode { namespace gs1 {

struct code_validator
{
	virtual bool check_code( boost::uint16_t number_ ) const = 0;

	virtual ~code_validator(){}
};

typedef boost::shared_ptr< code_validator > code_validator_ptr;

struct code_number
	: public code_validator
{
	explicit code_number( boost::uint16_t number_ )
		: _number( number_ )
	{
	}

	virtual bool check_code( boost::uint16_t number_ ) const
	{
		return _number == number_;
	}

private:
	boost::uint16_t _number;
};

inline code_validator_ptr make_validator( boost::uint16_t number_ )
{
	return code_validator_ptr( new code_number( number_ ) );
}

struct code_range
	: public code_validator
{
	code_range( boost::uint16_t from_, boost::uint16_t to_ )
		: _from( from_ )
		, _to( to_ )
	{
	}

	virtual bool check_code( boost::uint16_t number_ ) const
	{
		return number_ >= _from && number_ <= _to;
	}

private:
	boost::uint16_t _from;
	boost::uint16_t _to;
};

inline code_validator_ptr make_validator( boost::uint16_t from_, boost::uint16_t to_ )
{
	return code_validator_ptr( new code_range( from_, to_ ) );
}

struct code_union
	: public code_validator
{
	code_union( code_validator_ptr first_, code_validator_ptr second_ )
		: _first( first_ )
		, _second( second_ )
	{
	}

	virtual bool check_code( boost::uint16_t number_ ) const
	{
		return _first->check_code( number_ )
			|| _second->check_code( number_ );
	}

private:
	code_validator_ptr _first;
	code_validator_ptr _second;
};

inline code_validator_ptr make_validator( code_validator_ptr first_, code_validator_ptr second_ )
{
	return code_validator_ptr( new code_union( first_, second_ ) );
}

struct country_code
{
	country_code( const std::string& country_, code_validator_ptr code_ )
		: _country( country_ )
		, _code( code_ )
	{
	}

	bool check_code( boost::uint16_t number_ ) const
	{
		return _code->check_code( number_ );
	}

	const std::string& country() const
	{
		return _country;
	}

private:
	std::string _country;
	code_validator_ptr _code;
};

typedef boost::shared_ptr< country_code > code_ptr;

inline code_ptr make_code( const std::string& country_, code_validator_ptr code_ )
{
	return code_ptr( new country_code( country_, code_ ) );
}

struct is_from_country
	: std::unary_function< code_ptr, bool >
{
	is_from_country( boost::uint16_t number_ )
		: _number( number_ )
	{}

	bool operator()( code_ptr code_ ) const
		{ return code_->check_code( _number ); }

private:
	boost::uint16_t _number;
};

class country_codes
	: private boost::noncopyable
{
private:
	typedef std::list< code_ptr > codes_type;

public:
	static country_codes& all();

	code_ptr find( boost::uint16_t number_ ) const;

private:
	country_codes();

	codes_type _codes;
};

country_codes::country_codes()
{
	_codes.push_back( make_code( "USA", make_validator( 0, 139 ) ) );
	_codes.push_back( make_code( "Japan", make_validator( make_validator( 450, 459 ), make_validator( 490, 499 ) ) ) );
	_codes.push_back( make_code( "Ukraine", make_validator( 482 ) ) );
}

code_ptr country_codes::find( boost::uint16_t number_ ) const
{
	codes_type::const_iterator it_
		= std::find_if( _codes.begin(), _codes.end(), is_from_country( number_ ) );

	if ( it_ == _codes.end() )
		return code_ptr();

	return *it_;
}

country_codes& country_codes::all()
{
	static country_codes codes_;
	return codes_;
}

}}

#endif //BARCODE_COUNTRY_CODE_HPP_INLCUDED
